﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks;
using Red.Core.Containers;
using Red.EngineData.View;

namespace Red.Controls.ViewModels
{
    public sealed class InteropTreeItem : InteropDataItem
    {
        #region Data

        private readonly ulong _nodeId;
        // dispatcher may want to access this map to enumerate children and it may as well be modified by AddChild/RemoveChild
        private ConcurrentDictionary<ulong, InteropTreeItem> _childrenMap = new ConcurrentDictionary<ulong, InteropTreeItem>();
        private IEnumerable<RedMenuDataItem> _menu;

        #endregion Data

        #region Properties

        /// <summary>
        ///     This node's unique backend ID
        /// </summary>
        public ulong NodeID { get { return _nodeId; } }

        /// <summary>
        ///     This node's children count
        /// </summary>
        public int ChildrenCount { get { return _childrenMap.Count; } }

        /// <summary>
        ///     This nodes context menu hierarchy
        /// </summary>
        public override IEnumerable<RedMenuDataItem> Menu { get { return _menu; } }

        #endregion Properties

        #region C-tor

        public InteropTreeItem( DataView view, InteropDataItem parent, ulong nodeId )
            : base( parent, view, "<item>", null, null )
        {
            IsValid = true;

            _nodeId = nodeId;
            _menu = new List<RedMenuDataItem>();

            // initialize
            Data.Execute( Interop.Make( "name" ), result => Title = result.Data );

            PropertyChanged += OnPropertyChanged;
        }

        #endregion C-tor

        #region Public methods

        /// <summary>
        ///     Request node data to be added with next rebuild
        /// </summary>
        /// <param name="nodeId"></param>
        public void AddChild( InteropTreeItem node )
        {
            _childrenMap.TryAdd( node.NodeID, node );
        }

        /// <summary>
        ///     Request node data to be removed with next rebuild
        /// </summary>
        /// <param name="nodeId"></param>
        public void RemoveChild( ulong nodeId )
        {
            InteropTreeItem removed;
            _childrenMap.TryRemove( nodeId, out removed );
        }

        /// <summary>
        ///     Inject context menu specific for a particular type of tree item
        /// </summary>
        /// <param name="menu"></param>
        public void SetMenu( IEnumerable< RedMenuDataItem > menu )
        {
            _menu = menu;
        }

        #endregion Public methods

        #region Private methods

        private void OnPropertyChanged( object sender, PropertyChangedEventArgs e )
        {
            if ( e.PropertyName == "IsExpanded" )
            {
                if ( IsExpanded )
                    Parent.Execute( "expandNodes", _nodeId );
                else
                    Parent.Execute( "collapseNodes", _nodeId );
            }
        }

        #endregion Private methods

        #region DataItem interface

        protected override Task EnumerateChildrenAsync( IRangedCollection<DataItem> children )
        {
            var lastNodes = new Dictionary<ulong, InteropTreeItem>();
            foreach ( var child in children )
            {
                var treeItem = child as InteropTreeItem;
                lastNodes.Add( treeItem.NodeID, treeItem );
            }

            foreach ( var node in _childrenMap.Values )
            {
                var nodeId = node.NodeID;

                InteropTreeItem lastNode = null;
                lastNodes.TryGetValue( nodeId, out lastNode );

                if ( lastNode == null )
                {
                    // new node
                    children.Add( node );
                }
                else
                {
                    // already exist
                    lastNodes.Remove( nodeId );
                }
            }

            foreach ( var node in lastNodes.Values )
            {
                // removed
                children.Remove( node );
            }

            return Task.FromResult( true );
        }

        #endregion DataItem interface
    }
}
